package com.common.csv;

import com.opencsv.bean.*;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {

    /**
     *
     *
     * @return
     * @throws CsvRequiredFieldEmptyException
     */

    @Override
    public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {

        Field[] fields = FieldUtils.getAllFields(bean.getClass());
        if (fields.length == 0){
            return new String[]{};
        }

        List<Field> fieldList =  Stream.of(fields).filter(f->isCsvPosition(f) || isCsvName(f)).collect(Collectors.toList());
        String[] headerColumns = new String[fieldList.size()];
        AtomicInteger index = new AtomicInteger(0);
        fieldList.stream()
                .filter(f->isCsvPosition(f))
                .sorted(Comparator.comparing(o->getPositionIndex(o)))
                .forEach(o->{
                    headerColumns[index.getAndIncrement()] = o.getName();
                });


        fieldList.stream()
                .filter(f->!isCsvPosition(f))
                .forEach(o->{
                    headerColumns[index.getAndIncrement()] = o.getName();
                });

        // Always take what's been given or previously determined first.
        if(headerIndex.isEmpty()) {
//            String[] header = getFieldMap().generateHeader(bean);
            headerIndex.initializeHeaderIndex(headerColumns);
//            return header;
        }

        initializeFieldMap();

        return headerColumns;
    }

//    @Override
//    protected Set<Class<? extends Annotation>> getBindingAnnotations() {
//        Set<Class<? extends Annotation>> set = super.getBindingAnnotations();
//        set.add(CsvBindByName.class);
//        return set;
//    }

    //    @Override
//    protected void loadAnnotatedFieldMap(ListValuedMap<Class<?>, Field> fields) {
//        super.loadAnnotatedFieldMap(fields);
//    }

    private String extractHeaderName(final BeanField beanField) {
        if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) {
            return StringUtils.EMPTY;
        }

        final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0];
        return bindByNameAnnotation.column();
    }

    private boolean isCsvPosition(Field f){
        if (f.getAnnotation(CsvCustomBindByPosition.class) != null
                || f.getAnnotation(CsvBindAndSplitByPosition.class) != null
                || f.getAnnotation(CsvBindByPosition.class) != null) {
            return true;
        }
        return false;
    }

    private boolean isCsvName(Field f){
        if (f.getAnnotation(CsvBindByName.class) != null
                || f.getAnnotation(CsvBindByNames.class) != null
                || f.getAnnotation(CsvBindAndSplitByName.class) != null
                || f.getAnnotation(CsvCustomBindByName.class) != null) {
            return true;
        }
        return false;
    }

    private int getPositionIndex(Field f){
        if (f.getAnnotation(CsvCustomBindByPosition.class) != null){
            return f.getAnnotation(CsvCustomBindByPosition.class).position();
        }
        if (f.getAnnotation(CsvBindAndSplitByPosition.class) != null){
            return f.getAnnotation(CsvBindAndSplitByPosition.class).position();
        }
        if (f.getAnnotation(CsvBindByPosition.class) != null){
            return f.getAnnotation(CsvBindByPosition.class).position();
        }
        return 0;
    }

}
